/*
 * ENUM Discoverer. ENUM Demonstration software, demonstrating the potential of ENUM.
 * 
 * Copyright (C) 2009 SIDN and ISOC.nl
 * 
 * This file is part of ENUM Discoverer.
 *
 * ENUM Discoverer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ENUM Discoverer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ENUM Discoverer.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.sidn.enumdiscoverer.contacts;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;

import android.util.Log;

import nl.sidn.enumdiscoverer.LogConfig;

/**
 * Maps ENUM records to Contact fields
 * @author	Grant Patterson (grant.patterson@catalyst.net.nz)
 */
public class EnumToContactMapper {

	static final String TAG = LogConfig.getLogTag(EnumToContactMapper.class);
	// whether DEBUG level logging is enabled (whether globally, or explicitly for this log tag)
	static final boolean DEBUG = LogConfig.isDebug(TAG);
	// whether VERBOSE level logging is enabled
	static final boolean VERBOSE = LogConfig.VERBOSE;
	
	// Rules for mapping ENUM records to contact fields, indexed by enumservice type
	private final HashMap<String, ContactMappingRule[]> mRules = new HashMap<String, ContactMappingRule[]>();
	
	// precondition: mappingRules contains no null elements
	public EnumToContactMapper(ArrayList<ContactMappingRule> mappingRules) {
		
		if (mappingRules == null || mappingRules.size() == 0)
			return;
		
		// stack reference to commonly accessed field
		HashMap<String, ContactMappingRule[]> rulesMap = mRules;
		
		Collections.sort(mappingRules, new RuleTypeComparator());
		
		ContactMappingRule current = mappingRules.get(0);
		String prevType;
		
		ArrayList<ContactMappingRule> rulesTemp = new ArrayList<ContactMappingRule>(5);
		rulesTemp.add(current);
		
		int size = mappingRules.size();
		for (int i = 1; i < size; i++) {
			
			prevType = current.getType();
			current = mappingRules.get(i);
			
			if (!current.getType().equals(prevType)) {
				// we've finished with the previous type. Put in rulesMap and clear rulesTemp
				ContactMappingRule[] arr = new ContactMappingRule[rulesTemp.size()];
				rulesTemp.toArray(arr);
				rulesMap.put(prevType, arr);
				
				rulesTemp.clear();
			}
			
			rulesTemp.add(current);
		}
		
		// Copy the last type from rulesTemp to mRules
		ContactMappingRule[] arr = new ContactMappingRule[rulesTemp.size()];
		rulesTemp.toArray(arr);
		rulesMap.put(current.getType(), arr);
	}
	
	/**
	 * Find the contact adapters to handle this enumService and URL.
	 * @param enumService The enumservice field of the ENUM record
	 * @param url The url produced from the ENUM record
	 * @return The results of all mapping rules that match enumService and url. null if none.
	 */
	public ArrayList<MappingResult> doMapping(String enumService, String url) {
		
		if (VERBOSE) Log.v(TAG, "Getting contact adapters matching (" + enumService + ", " + url + ")");
		
		// we're not interested in subtype, since mRules in indexed by type
		int index = enumService.indexOf(':');
		String type = index == -1 ? enumService : enumService.substring(0, index);
		
		ContactMappingRule[] rules = mRules.get(type);
		if (rules == null) {
			return null;
		}
		
		// The adapters from the matching ContactMappingRules
		ArrayList<MappingResult> results = new ArrayList<MappingResult>();
		
		for (ContactMappingRule rule : rules) {
			MappingResult result = rule.doMapping(enumService, url);
			if (result != null) {
				results.add(result);
				if (VERBOSE) Log.v(TAG, "Found matching rule: " + rule);
			}
		}
		
		return results;
	}
	
	/**
	 * Compares two ContactMappingRules using only lexicographic comparison of
	 * their respective enumservice types.
	 */
	private static class RuleTypeComparator implements Comparator<ContactMappingRule> {
		@Override
		public int compare(ContactMappingRule rule1, ContactMappingRule rule2) {
			return rule1.getType().compareTo(rule2.getType());
		}
	}
}
